本篇同步發布於Blog: [解題] POJ - 1182 食物链
PKU Online Judge
1182 - 食物链
http://poj.org/problem?id=1182
這題是中文題目,可以看原文的敘述。這裡再簡化敘述,有從1...N的N個編號物種,每個物種可能歸類在A、B、C其中一種,A會吃B、B會吃C、C會吃A。
接著有K筆敘述,每一筆有D、X、Y,D等於1時是X和Y同物種,D等於2時是X會吃Y。
但這K筆敘述不完全是對的,要找出後面敘述與前面敘述矛盾的筆數。比如前面第1筆敘述1和2同物種,但之後第2筆敘述說2能吃1,這樣是矛盾的。除了矛盾之外,也要檢查物種編號是否在1...N之間,否則也要歸類矛盾的筆數。
100 7
1 101 1
2 1 2
2 2 3
2 3 3
1 1 3
2 3 1
1 5 5
3
這題可用Union and Find,將物種與食物鏈都用同一種分組的方式。由於有分A、B、C三個物種,於是用偏移量 N 與 2*N 代表 B與C。
一開始先初始化,每個物種都只屬於自己的編號,尚未與別人做分組。
每一筆敘述,如果X與Y是同一種,同時建立X-A與Y-A、X-B與Y-B、X-C與Y-C的群組,但建立這群組前要先檢查是否X-A與Y-B的群組已存在或者X-A與Y-C的群組已存在,否則這X與Y是同一種的敘述是矛盾的。
如果X與Y是X吃Y,同時建立X-A與Y-B、X-B與Y-C、X-C與Y-A的群組,但建立這群組前要先檢查是否X-A與Y-A的群組已存在或者X-A與Y-C的群組已存在,否則這X吃Y的敘述是矛盾的。
以範例輸入來看:
所以一共有3筆敘述是矛盾的,回傳3。
題目輸入有些陷阱,比如資料量過大,要用scanf()而不是cin,否則會超時;另外不要用while()讀測資,只要讀一次就好,否則會WA。。。
難度為Medium
#include <iostream>
#include <cstdio>
#define MAXN 150005
using namespace std;
int ranks[MAXN];
int pre[MAXN];
void init(int n)
{
for(int i = 0 ; i < n; ++i)
{
pre[i] = i;
ranks[i] = 0;
}
}
int findl(int x)
{
if(pre[x] == x)
{
return x;
}
else
{
return pre[x] = findl(pre[x]);
}
}
void unite(int x, int y)
{
x = findl(x);
y = findl(y);
if(x == y)
{
return;
}
if(ranks[x] < ranks[y])
{
pre[x] = y;
}
else
{
pre[y] = x;
if(ranks[x] == ranks[y])
{
ranks[x]++;
}
}
}
bool same(int x, int y)
{
return findl(x) == findl(y);
}
int main()
{
int N, K;
int D, X, Y;
scanf("%d %d", &N, &K);
int ans = 0;
init(N * 3);
for(int i = 0 ; i < K; ++i)
{
scanf("%d %d %d", &D, &X, &Y);
X = X - 1;
Y = Y - 1;
if(X < 0 || X >= N || Y < 0 || Y >= N)
{
ans++;
continue;
}
if(D == 1)
{
if(same(X, Y + N) || same(X, Y + 2 * N))
{
ans++;
}
else
{
unite(X, Y);
unite(X + N, Y + N);
unite(X + 2 * N, Y + 2 * N);
}
}
else if(D == 2)
{
if(same(X, Y) || same(X, Y + 2 * N))
{
ans++;
}
else
{
unite(X, Y + N);
unite(X + N, Y + N * 2);
unite(X + 2 * N, Y);
}
}
}
printf("%d\n", ans);
return 0;
}
https://github.com/u8989332/ProblemSolving/blob/master/PKUOnlineJudge/1100-1199/1182.cpp